package com.shanshan.wheel.rpc.common.provider;

import com.shanshan.wheel.rpc.common.exception.RpcErrorMessage;
import com.shanshan.wheel.rpc.common.exception.RpcException;
import com.shanshan.wheel.rpc.common.registry.ServiceRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description 服务提供者实现类
 * @ClassName ServiceProviderImpl
 * @Author 山山大魔王
 * @Date 2022/9/4 15:13
 * @Version 1.0
 */
@Component
@Slf4j
public class ServiceProviderImpl implements ServiceProvider{
    /**
     * 服务的实现类存储再服务提供方的一个Map中
     * 服务消费者通过远程请求在Map中查询得到服务的实现类并返回
     * 注意：每个服务器只会在Map中存放属于自己的实现类（不会存放属于其他Server的实现类）
     *       服务消费者在请求服务时也只会路由到某个实现服务的server上！！
     *       因此Map中只存放属于自己的实现类是合理的
     *
     */
    private final Map<String , Object> serviceMap;

    private final Set<String> registeredService;

    private static Integer PORT;

    @Value("${server.port}")
    private Integer server_port;

    @PostConstruct
    public void getEnvironment(){
        PORT = server_port;
    }

    @Autowired
    private ServiceRegistry serviceRegistry;

    public ServiceProviderImpl(){
        serviceMap = new ConcurrentHashMap<>();
        registeredService = ConcurrentHashMap.newKeySet();
    }


    @Override
    public void publishService(Object service) {
        this.publishService(service, RpcServiceProperties.builder().group("").version("").build());
    }

    /**
     * 发布服务
     * @param service
     * @param rpcServiceProperties
     */
    @Override
    public void publishService(Object service, RpcServiceProperties rpcServiceProperties) {

        try {
            String host = InetAddress.getLocalHost().getHostAddress();
            // 该实现类注册的服务接口
            Class<?> serviceRaletedInterface = service.getClass().getInterfaces()[0];
            // 获取服务接口名称
            String serviceName = serviceRaletedInterface.getCanonicalName();
            // 设置服务名
            rpcServiceProperties.setServiceName(serviceName);
            // 将服务在本地保存
            this.addService(service, rpcServiceProperties);
            // 将服务在zk进行注册
            serviceRegistry.registerService(rpcServiceProperties.getServiceName(), new InetSocketAddress(host, PORT));

            log.info("服务已成功注册：{}",rpcServiceProperties.toRpcServiceName());
        } catch (UnknownHostException e) {
            log.error("getLocalHost方法发生异常", e);
        }
    }

    /**
     * 获取本地服务
     * @param rpcServiceProperties
     * @return
     */
    @Override
    public Object getService(RpcServiceProperties rpcServiceProperties) {
        // 从map中获取服务名映射的对象
        Object service = serviceMap.get(rpcServiceProperties.toRpcServiceName());
        if(null == service){
            throw new RpcException(RpcErrorMessage.SERVICE_CAN_NOT_BE_FOUND);
        }
        return service;
    }

    /**
     * 在本地保存好服务
     * @param service
     * @param rpcServiceProperties
     */
    private void addService(Object service, RpcServiceProperties rpcServiceProperties){
        // 获取服务名
        String rpcServiceName = rpcServiceProperties.getServiceName();
        // 判断服务名是否在本机注册
        if(registeredService.contains(rpcServiceName)){
            return;
        }
        // 将服务名添加到注册服务的set
        registeredService.add(rpcServiceName);
        // 将服务名->服务实例化对象 添加到map
        serviceMap.put(rpcServiceName, service);
        log.info("添加服务：{} 和接口：{}",rpcServiceName, service.getClass().getInterfaces()[0]);
    }
}
